#ifndef __REDCELL_DATA_HPP__
#define __REDCELL_DATA_HPP__

#include <string>
#include <sstream>
#include <map>
#include "common.hpp"

#ifdef __RC_WITHBOOST__
#include <boost/pool/pool_alloc.hpp>
#endif

namespace redcell {

  // TableImpl

  struct Variant;

#ifdef __RC_WITHBOOST__
    typedef std::map<Key, Variant, std::less<Key>, 
                     boost::fast_pool_allocator<std::pair<const Key, Variant> > > TableMap;
#else
    typedef std::map<Key, Variant> TableMap;
#endif


  struct TableImpl: public TableMap {
    size_t cnt;
    Oid oid;

    TableImpl() {
      cnt = 0;
    }
      
    void incRef() {
      ++cnt;
    }

    void decRef() {
      if (!--cnt) {
	delete this;
      }
    }
  };


  // Table

  struct Table {
    TableImpl *impl;

    typedef TableImpl::iterator iterator;
    typedef TableImpl::const_iterator const_iterator;
    typedef TableImpl::reverse_iterator reverse_iterator;
    typedef TableImpl::const_reverse_iterator const_reverse_iterator;

    Table() {
      impl = new TableImpl;
      impl->incRef();
    }

    Table(const Table &rhs) {
      impl = rhs.impl;
      impl->incRef();
    }

    ~Table() {
      impl->decRef();
    }

    Table &operator =(const Table &rhs) {
      impl->decRef();
      impl = rhs.impl;
      impl->incRef();
    }

    TableImpl *operator ->() const {
      return impl;
    }

    const Variant &findItem(const Key key) const;
    Variant &ensureItem(const Key key);

    Integer &i(const Key key);
    Real &r(const Key key);
    String &s(const Key key);
    Table &t(const Key key);

    Table &operator ()(const Key key) { return t(key); }

    const Integer &ci(const Key key) const;
    const Real &cr(const Key key) const;
    const String &cs(const Key key) const;
    const Table &ct(const Key key) const;

    const Integer &i(const Key key) const { return ci(key); }
    const Real &r(const Key key) const { return cr(key); }
    const String &s(const Key key) const { return cs(key); }
    const Table &t(const Key key) const { return ct(key); }

    const Table &operator ()(const Key key) const { return ct(key); }

    void nil(const Key key);
    void del(const Key key);

    char type(const Key key) const;

    void merge(const Table &patch);
  };



  // Variant


  struct Variant {
    char vartype;

#define UNION_MEMBERS					\
    Integer integer;					\
    Real real;						\
    char stringBuffer[sizeof(String)];			\
    char tableBuffer[sizeof(Table)];

    union {
      UNION_MEMBERS
      union {
        UNION_MEMBERS
      } all;
    };

    String &string() { return *reinterpret_cast<String *>(stringBuffer); }
    Table &table() { return *reinterpret_cast<Table *>(tableBuffer); }

    const String &string() const { return *reinterpret_cast<const String *>(stringBuffer); }
    const Table &table() const { return *reinterpret_cast<const Table *>(tableBuffer); }

    void freeRef() {
      switch (type()) {
      case STRING: string().~String(); break;
      case TABLE: table().~Table(); break;
      }
    }

    void checkType(const char type) const {
      if (type != this->type()) {
	throw TypeError(type, this->type());
      }
    }

    void ensureType(const char type) {
      if (type == this->type()) {
	return;
      }
      freeRef();
      switch (this->vartype = type) {
      case STRING: new(&string()) String; break;
      case TABLE: new(&table()) Table; break;
      }
    }


    Integer &i() { ensureType(INTEGER); return integer; }
    Real &r() { ensureType(REAL); return real; }
    String &s() { ensureType(STRING); return string(); }
    Table &t() { ensureType(TABLE); return table(); }

    Table &operator ()() { return t(); }

    const Integer &ci() const { checkType(INTEGER); return integer; }
    const Real &cr() const { checkType(REAL); return real; }
    const String &cs() const { checkType(STRING); return string(); }
    const Table &ct() const { checkType(TABLE); return table(); }

    const Integer &i() const { return ci(); }
    const Real &r() const { return cr(); }
    const String &s() const { return cs(); }
    const Table &t() const { return ct(); }

    const Table &operator ()() const { return ct(); }

    void nil() {
      freeRef();
      vartype = NIL;
    }

    char type() const {
      return vartype;
    }

    void setVariant(const Variant &rhs) {
      switch (vartype = rhs.type()) {
      case STRING: new(&string()) String(rhs.string()); break;
      case TABLE: new(&table()) Table(rhs.table()); break;
      default: all = rhs.all; break;
      }
    }

    Variant() {
      vartype = NIL;
    }

    Variant(const Variant &rhs) {
      setVariant(rhs); 
    }

    ~Variant() {
      freeRef();
    }

    Variant &operator =(const Variant &rhs) {
      if (&rhs != this) {
        freeRef();
        setVariant(rhs);
      }
      return *this;
    }
  };


  // methods of Table

  inline const Variant &Table::findItem(const Key key) const {
    TableImpl::iterator it = impl->find(key);
    if (it == impl->end()) {
      throw KeyError(key);
    }
    return it->second;
  }

  inline Variant &Table::ensureItem(const Key key) {
    return (*impl)[key];
  }

  inline Integer &Table::i(const Key key) { return ensureItem(key).i(); }
  inline Real &Table::r(const Key key) { return ensureItem(key).r(); }
  inline String &Table::s(const Key key) { return ensureItem(key).s(); }
  inline Table &Table::t(const Key key) { return ensureItem(key).t(); }

  inline const Integer &Table::ci(const Key key) const { return findItem(key).ci(); }
  inline const Real &Table::cr(const Key key) const { return findItem(key).cr(); }
  inline const String &Table::cs(const Key key) const { return findItem(key).cs(); }
  inline const Table &Table::ct(const Key key) const { return findItem(key).ct(); }
  
  inline void Table::nil(const Key key) { ensureItem(key).nil(); }
  inline void Table::del(const Key key) { impl->erase(key); }


  inline char Table::type(const Key key) const {
    return findItem(key).type();
  }

  inline void Table::merge(const Table &patch) {
    for (TableImpl::const_iterator i=patch->begin(); i!=patch->end(); ++i) {
      if (i->second.type() == TABLE) {
	ensureItem(i->first).t().merge(i->second.table());
      } else {
	ensureItem(i->first) = i->second;
      }
    }
  }

}

#endif
